Communication Patterns in Angular
How data flows in Angular
Angular follows a two-way data flow pattern, meaning you can send data up and down the component tree. As everything in the Angular is a component, understanding communication between components is crucial for every successful Angular project.
In this post, I will cover all the ways we can communicate between components. These are the topics we will go through:
- Example Project
- Component Basics
- Parent to Child Communication
- Child to Parent Communication
- Sibling Communication With Services
- Sibling Communication With EventEmitter
- Communication Using @ViewChild Decorator
- Communication Using Content Projection
- Communication With NGRX Store
- Communication Between Modules
- Summary
- Conclusion
Example Project
Here is the example project, so you can see all the communication in action. You can clone this project to test it on your machine.
// clone the project
git clone https://github.com/bbachi/angular-communication.git// install all the dependencies and start the project
npm install
npm startComponent basics
I want to go over the basics of components, before diving into the communications between them. If you are already familiar with these concepts you can jump to the next section.
Angular follows component design pattern, which means that in Angular everything is a component. Your entire application is divided into modules and each module can be divided into small, reusable components. If you look at the diagram below, we have defined three modules and each module has a number of components. This module system helps load the app faster as only the necessary modules are loaded in the browser.
Usually we have components inside components to form the larger component, called composition. In the following diagram we have header, footer and dashboard components inside theapp component which forms the app module.
Module
Every component should be declared in the module by importing and place that in the declarations array of the module, as shown below:
Component
Every component has three important files: class file app.component.ts for the logic, html file app.component.html for the presentation, and CSS file app.component.css for the styles.
Notice the selector in the above file. This value, app-root, is used to place the component in the DOM like this:
Interpolation
Interpolation is the way to inject some value into HTML. We are injecting title from the component class to the HTML.
<h1>
Welcome to {{ title }}!
</h1>Property Binding
Property Binding is used to send the information from the component class to view with the square brackets []. In the example below example, we are passing title property to the header component.
<app-header [title]="title"></app-header>Event Binding
Event Binding is used to send the information from the view class to component class with the normal brackets (). In the example below we are passing an event from the header component.
<app-header (click)="updateHeader()"></app-header>With the basics out of the way, let’s dive into communication patterns in Angular.
Parent to child communication
Let’s see how we can pass information from the parent component to the child component. We have a title which will be passed from the parent component (app) to child (header) component.
We define the title in the app component as shown below. Let’s see how we pass this title to the header component.
export class AppComponent {
title = 'angular-communication';
}We have @Input() headerTitle in the header and we use property binding [headerTitle]in the app component to pass the title.
Here is the output in the browser. So, with the @Input() decorator we can pass data to the child component.
Child to Parent Communication
Let’s see how we pass information from the child component to the parent component.
Let’s add some links in the header (child). When you click the link, the link name is displayed in the app component (parent) under the title “Welcome to App!” as shown below.
We have @Output decorator and EventEmitterto send the information from the child component to the parent component.
We define navOut an eventEmitter — when you click on the link we emit the value.
When the event is emitted, it will be captured here in the app component as shown below. We define onNavigation() method in the app component to capture the emitted value and assign that to linkName property. We can place this linkName property in the HTML file with an interpolation {{linkName}}.
Sibling communication with services
Components at the same level are called sibling components. In our example, Header and Footer are the sibling components. In this section, let’s see how we can communicate between sibling components with the help of services.
If we look at the below diagram, we define Subject in the services which receives the data from the Header component. Footer component subscribes to the Subject defined in the services and receives data as soon as data is emitted by Subject with the next function.
Let’s see that in action. We have header links and we have the same links in the footer as quick links. But we don’t have the dashboard link in the footer, because the user is not logged in.
Clicking on the login link in the header logs the user in and changes the login link to a logout link. We need to communicate to the footer so it displays the dashboard link and logout instead of login. See below for the logged-in state:
Here is an example code for the services, Header and Footer components. We define theisUserLoggedIn subject and setUserLoggedIn method:
Here are the Header and Footer components. I have removed other code for brevity. We Injected service in both components and we use it in the Header component to send the data and use it in the Footer component to receive data.
Here is the footer template:
Sibling communication with EventEmitter
In the above section, we saw how we can communicate between sibling components with the help of services. Let’s see how we can do the same thing with EventEmitter.
If we look at the below diagram, we send the data with the EventEmitter to the App component. We set the App properties with the data received from the Header. As soon as App properties are set, Footer receives the data as the Input.
Let’s see that in action with the below code. I removed the code in between. Header is emitting the event setLoggedInFlag and loginFlag is passed to footer:
Here is the Footer component with getters and setters for the input to detect the changes:
Communication using @ViewChild decorator
@ViewChild is one of the common decorators we use in Angular. With this we can obtain the reference of the custom angular component by querying the template.
We have a parent component called ParentComponent and a child component called ChildComponent. We want to set the title property of the child component, but the title is only available in the parent component for some reason — for instance, after making an API call.
Let’s see how we can do this with the @ViewChild decorator. Here is the parent component where we can access the child component property title with the help of @ViewChild decorator and we need to use ngAfterViewInit lifecycle method to initialize the child component title.
Here is the child component:
When we place the parent component in the app component, here is the output in the browser:
This is one of the use cases with the @ViewChild decorator.
Communication using content projection
We have seen communication with help of property binding, event binding, and decorators. There is another way to send data to the child component — by placing content between component tags as shown:
<app-welcome-message>
<h2>Welcome to APP!</h2>
</app-welcome-message>This is the code for the app-welcome-message component html:
<div>
<ng-content></ng-content>
</div>We can project the content into components with the help of ng-content:
Here is the output in the browser:
Communication with NGRX store
NGRX is a state management tool inspired by redux for the Angular Applications. When your application gets bigger communication becomes difficult to handle. NGRX provides unidirectional data flow and a single source of truth for the entire app.
Take a look at the diagram below: there are smart components and dumb components. Smart components are those that are aware of the store, subscribe to it, receive the data and pass it along to the dumb components. On the other hand, dumb components are presentational components that receive data with the Input decorator and send data out with EventEmitter.
Communication with NGRX Store in detail
Let’s explore the most important aspects of how the data actually flows with NGRX Store.
Smart components are the ones that subscribe to the store, dispatch an action, receives the new state., as you can see in this diagram:
Actions are the just javascript objects containing type and payload. Here’s an example:
{
"type":"LOGIN",
"payload": data
}Action Creators are javascript functions which take an action as an input and perform the appropriate action, such as calling reducers or calling API through NGRX effects.
Reducers are the pure functions which take the action and payload as arguments and return the new state.
Here is an example. The bottom of the file has selectors which are the special functions which give you the particular slice of the State:
This is how we subscribe to the store from the components — we have to inject the store into the component’s constructor. We can dispatch an action with thethis.store.dispatch method and receive the data from the store with the selectors, this.store.select.
Communication between modules
The entire Angular App is divided into modules based on application features. Sometimes we need communication among modules as well.
There are a couple of ways we can do that.
NGRX Store
As we discussed earlier, we can subscribe to the store from the components and share data. Any component from any module that can access the store and dispatch an action receives data.
Take a look at the diagram below. We can dispatch an action from component A in Module A with data to make the change in the NGRX state. Component B from Module B subscribes to the store and receives that data as state changes.
Shared module
We can have a shared module which is imported in Modules and use the services to share data. As you can see in the next diagram, we have a shared module with services which can be used all the modules, provided we import the shared module into feature modules.
Summary
- Use
@Input()decorator for the parent to child communication. - Use
@Output()Decorator andEventEmitterfor a child to parent communication. ServicesandEventEmitterare two ways sibling components can communicate.- We can access child component by querying the template with
@ViewChild() - We can access multiple child components with
@ViewChildren() - It is always recommended to use NGRX state management for unidirectional data flow as the project gets bigger and bigger.
- We can create a shared module to facilitate communication between modules.
- Alternatively, we can use NGRX store for communication among modules.
Conclusion
Data flow is important in single page applications. Understanding these communication patterns is crucial for the success of your project. Without these basic concepts, you will end up with “spaghetti code”: hard to debug, head to read and hard to extend.